package org.opens.util;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sound.sampled.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class PcmUtils {

    public static void wirteWavBytes2OutputStream(byte[] bytes, HttpServletRequest request, HttpServletResponse response) {
        ByteArrayInputStream inputStream = null;
        BufferedInputStream bufferedInputStream = null;
        OutputStream outputStream = null;

        try {
            inputStream = new ByteArrayInputStream(bytes);
            bufferedInputStream = new BufferedInputStream(inputStream);
            int length = bytes.length;
            outputStream = response.getOutputStream();

            String range = request.getHeader("Range");
            String[] rs = range.split("\\=");
            range = rs[1].split("\\-")[0];

            byte[] buf = new byte[1024 * 4];
            int size = 0;

            // 播放进度
            int count = 0;
            // 播放百分比
            int percent = (int)(length * 1);
            int irange = Integer.parseInt(range);

            response.addHeader("Accept-Ranges", "bytes");
            response.addHeader("Content-Length", length + "");
            response.addHeader("Content-Range", "bytes " + irange + "-" + (length - 1) + "/" + length);
            response.addHeader("Content-Type", "audio/mpeg;charset=UTF-8");

            while ((size = bufferedInputStream.read(buf)) != -1) {
                //length += size;
                outputStream.write(buf, 0, size);
            }

            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(bufferedInputStream != null) {
                try {
                    bufferedInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

        /**
         * 功能:<br>
         * 自动将输入流写入response的输出流.<br>
         * 注意:<br>
         * 1. 现在可以控制播放进度了.
         * @param inputStream   wav文件的输入流
         */
    public static void wirteWavInputStream2OutputStream(InputStream inputStream, HttpServletRequest request, HttpServletResponse response) {
        BufferedInputStream fis = null;
        OutputStream outputStream = null;
        try {
            fis = new BufferedInputStream(inputStream);
            outputStream = response.getOutputStream();

            String range = request.getHeader("Range");
            String[] rs = range.split("\\=");
            range = rs[1].split("\\-")[0];

            int length = inputStream.available();
            byte[] buf = new byte[1024 * 4];
            int size = 0;

            // 播放进度
            int count = 0;
            // 播放百分比
            int percent = (int)(length * 1);
            int irange = Integer.parseInt(range);

            response.addHeader("Accept-Ranges", "bytes");
            response.addHeader("Content-Length", length + "");
            response.addHeader("Content-Range", "bytes " + irange + "-" + (length - 1) + "/" + length);
            response.addHeader("Content-Type", "audio/mpeg;charset=UTF-8");

            while ((size = fis.read(buf)) != -1) {
                //length += size;
                outputStream.write(buf, 0, size);
            }

            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void writePcmUrlFile2OutputStream(String urlFile, HttpServletRequest request, HttpServletResponse response) {
        BufferedInputStream fis = null;
        OutputStream outputStream = null;
        try {
            // 统一资源
            URL url = new URL(urlFile);
            // 连接类的父类，抽象类
            URLConnection urlConnection = url.openConnection();
            // http的连接类
            HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
            //设置超时
            httpURLConnection.setConnectTimeout(1000*10);
            //设置请求方式，默认是GET
            //httpURLConnection.setRequestMethod("POST");
            // 设置字符编码
            httpURLConnection.setRequestProperty("Charset", "UTF-8");
            //获取远程文件的大小
            int length = httpURLConnection.getContentLength();

            // 打开到此 URL引用的资源的通信链接（如果尚未建立这样的连接）。
            httpURLConnection.connect();

            // 建立链接从请求中获取数据
            URLConnection con = url.openConnection();
            fis = new BufferedInputStream(httpURLConnection.getInputStream());

            outputStream = response.getOutputStream();

            String range = request.getHeader("Range");
            String[] rs = range.split("\\=");
            range = rs[1].split("\\-")[0];

            int PCMSize = length;
            byte[] buf = new byte[1024 * 4];
            int size = 0;

            // 播放进度
            int count = 0;
            // 播放百分比
            int percent = (int)(length * 1);
            int irange = Integer.parseInt(range);

            response.addHeader("Accept-Ranges", "bytes");
            response.addHeader("Content-Length", length + "");
            response.addHeader("Content-Range", "bytes " + irange + "-" + (length - 1) + "/" + length);
            response.addHeader("Content-Type", "audio/mpeg;charset=UTF-8");

            //如果是pcm语音则填入参数，比特率等等。这里用的是16位单声道 8000 hz
            if(urlFile.contains(".pcm")) {
                WaveHeader header = new WaveHeader();
                //长度字段 = 内容的大小（PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)
                header.fileLength = PCMSize + (44 - 8);
                header.FmtHdrLeth = 16;
                header.BitsPerSample = 16;
                header.Channels = 1;
                header.FormatTag = 0x0001;
                header.SamplesPerSec = 8000;
                header.BlockAlign = (short) (header.Channels * header.BitsPerSample / 8);
                header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
                header.DataHdrLeth = PCMSize;

                byte[] h = header.getHeader();

                //WAV标准，头部应该是44字节
                assert h.length == 44;
                outputStream.write(h, 0, h.length);
            }
            //int totalSize = urlFile.contains(".pcm") ? 44 : 0;
            // 从Header中获取的Range本来是一个控制快进的参数, 本想想着是在这里控制一下跳到写出流的位置, 但是加上就会导致只写出部分.
            /*if(irange > 0) {
                fis.skip(irange);
            }*/
            while ((size = fis.read(buf)) != -1) {
                //totalSize += size;
                outputStream.write(buf, 0, size);
            }

            //System.out.println("修改后: " + length + ", 写出大小为: " + totalSize);
            outputStream.flush();
            outputStream.close();
            fis.close();
            httpURLConnection.disconnect();
        } catch (MalformedURLException e) {
            // LOGGER.error("获取了错误的录音文件url地址, url是: {}", urlFile);
        } catch (IOException e) {
            // LOGGER.error("获取的文件不存在或者是网络问题导致的, url是: {}", urlFile);
        } finally {
            if(fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void writeByte2OutputStream(byte[] bytes, HttpServletRequest request, HttpServletResponse response) {
        ByteArrayInputStream inputStream = null;
        BufferedInputStream bufferedInputStream = null;
        OutputStream outputStream = null;

        try {
            inputStream = new ByteArrayInputStream(bytes);
            bufferedInputStream = new BufferedInputStream(inputStream);
            int length = bytes.length;
            outputStream = response.getOutputStream();

            String range = request.getHeader("Range");
            String[] rs = range.split("\\=");
            range = rs[1].split("\\-")[0];

            int PCMSize = length;
            byte[] buf = new byte[1024 * 4];
            int size = 0;

            // 播放进度
            int count = 0;
            // 播放百分比
            int percent = (int)(length * 1);
            int irange = Integer.parseInt(range);

            response.addHeader("Accept-Ranges", "bytes");
            response.addHeader("Content-Length", length + "");
            response.addHeader("Content-Range", "bytes " + irange + "-" + (length - 1) + "/" + length);
            response.addHeader("Content-Type", "audio/mpeg;charset=UTF-8");

            WaveHeader header = new WaveHeader();
            //长度字段 = 内容的大小（PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)
            header.fileLength = PCMSize + (44 - 8);
            header.FmtHdrLeth = 16;
            header.BitsPerSample = 16;
            header.Channels = 1;
            header.FormatTag = 0x0001;
            header.SamplesPerSec = 8000;
            header.BlockAlign = (short) (header.Channels * header.BitsPerSample / 8);
            header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
            header.DataHdrLeth = PCMSize;

            byte[] h = header.getHeader();

            //WAV标准，头部应该是44字节
            assert h.length == 44;
            outputStream.write(h, 0, h.length);
            while ((size = bufferedInputStream.read(buf)) != -1) {
                outputStream.write(buf, 0, size);
            }

            //System.out.println("修改后: " + length + ", 写出大小为: " + totalSize);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            // LOGGER.error("输出流获取异常");
        } finally {
            if(bufferedInputStream != null) {
                try {
                    bufferedInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    public static void writePcmFile2OutputStream(File pcm, HttpServletRequest request, HttpServletResponse response) throws IOException {
        FileInputStream fis = null;
        OutputStream outputStream = null;
        try {
            fis = new FileInputStream(pcm);

            String range = request.getHeader("Range");
            String[] rs = range.split("\\=");
            range = rs[1].split("\\-")[0];

            int PCMSize = 0;
            byte[] buf = new byte[1024 * 4];
            int size = fis.read(buf);

            while (size != -1) {
                PCMSize += size;
                size = fis.read(buf);
            }

            fis.close();

            long length = pcm.length();
            // 播放进度
            int count = 0;
            // 播放百分比
            int percent = (int)(length * 1);
            int irange = Integer.parseInt(range);
            length = length - irange;

            response.addHeader("Accept-Ranges", "bytes");
            response.addHeader("Content-Length", length + "");
            response.addHeader("Content-Range", "bytes " + range + "-" + length + "/" + length);
            response.addHeader("Content-Type", "audio/mpeg;charset=UTF-8");

            outputStream = response.getOutputStream();
            //填入参数，比特率等等。这里用的是16位单声道 8000 hz
            WaveHeader header = new WaveHeader();
            //长度字段 = 内容的大小（PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)
            header.fileLength = PCMSize + (44 - 8);
            header.FmtHdrLeth = 16;
            header.BitsPerSample = 16;
            header.Channels = 1;
            header.FormatTag = 0x0001;
            header.SamplesPerSec = 8000;
            header.BlockAlign = (short) (header.Channels * header.BitsPerSample / 8);
            header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
            header.DataHdrLeth = PCMSize;

            byte[] h = header.getHeader();

            assert h.length == 44; //WAV标准，头部应该是44字节
            //write header

            outputStream.write(h, 0, h.length);
            //write data stream
            fis = new FileInputStream(pcm);
            size = fis.read(buf);
            while (size != -1) {
                outputStream.write(buf, 0, size);
                size = fis.read(buf);
            }
            fis.close();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    public static void pfPcm(byte[] audioData) {
            try {
                int offset = 0;
                int bufferSize = audioData.length;
                float sampleRate = 8000;
                int sampleSizeInBits = 16;
                int channels = 1;
                boolean signed = true;
                boolean bigEndian = false;
                // sampleRate - 每秒的样本数
                // sampleSizeInBits - 每个样本中的位数
                // channels - 声道数（单声道 1 个，立体声 2 个）
                // signed - 指示数据是有符号的，还是无符号的
                // bigEndian - 指示是否以 big-endian 字节顺序存储单个样本中的数据（false 意味着
                // little-endian）。
                AudioFormat af = new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
                SourceDataLine.Info info = new DataLine.Info(SourceDataLine.class, af, bufferSize);
                SourceDataLine sdl = (SourceDataLine) AudioSystem.getLine(info);
                sdl.open(af);
                sdl.start();
                while (offset < audioData.length) {
                    offset += sdl.write(audioData, offset, bufferSize);
                }
            } catch (LineUnavailableException e) {
                e.printStackTrace();
            }
    }


    public static class WaveHeader {

        public final char fileID[] = {'R', 'I', 'F', 'F'};
        public int fileLength;
        public char wavTag[] = {'W', 'A', 'V', 'E'};;
        public char FmtHdrID[] = {'f', 'm', 't', ' '};
        public int FmtHdrLeth;
        public short FormatTag;
        public short Channels;
        public int SamplesPerSec;
        public int AvgBytesPerSec;
        public short BlockAlign;
        public short BitsPerSample;
        public char DataHdrID[] = {'d','a','t','a'};
        public int DataHdrLeth;

        public byte[] getHeader() throws IOException {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            WriteChar(bos, fileID);
            WriteInt(bos, fileLength);
            WriteChar(bos, wavTag);
            WriteChar(bos, FmtHdrID);
            WriteInt(bos,FmtHdrLeth);
            WriteShort(bos,FormatTag);
            WriteShort(bos,Channels);
            WriteInt(bos,SamplesPerSec);
            WriteInt(bos,AvgBytesPerSec);
            WriteShort(bos,BlockAlign);
            WriteShort(bos,BitsPerSample);
            WriteChar(bos,DataHdrID);
            WriteInt(bos,DataHdrLeth);
            bos.flush();
            byte[] r = bos.toByteArray();
            bos.close();
            return r;
        }

        private void WriteShort(ByteArrayOutputStream bos, int s) throws IOException {
            byte[] mybyte = new byte[2];
            mybyte[1] =(byte)( (s << 16) >> 24 );
            mybyte[0] =(byte)( (s << 24) >> 24 );
            bos.write(mybyte);
        }


        private void WriteInt(ByteArrayOutputStream bos, int n) throws IOException {
            byte[] buf = new byte[4];
            buf[3] =(byte)( n >> 24 );
            buf[2] =(byte)( (n << 8) >> 24 );
            buf[1] =(byte)( (n << 16) >> 24 );
            buf[0] =(byte)( (n << 24) >> 24 );
            bos.write(buf);
        }

        private void WriteChar(ByteArrayOutputStream bos, char[] id) {
            for (int i=0; i<id.length; i++) {
                char c = id[i];
                bos.write(c);
            }
        }

    }

    public static void main(String[] args) throws IOException {
        WaveHeader waveHeader = new WaveHeader();
        System.out.println(waveHeader.getHeader().length);
    }
}
